springBoot聚合websocket如何实现单机10万+长连接

您所在的位置:网站首页 swoole websocket 最大连接数 springBoot聚合websocket如何实现单机10万+长连接

springBoot聚合websocket如何实现单机10万+长连接

2024-01-30 08:10| 来源: 网络整理| 查看: 265

1、springboot项目聚合websockert代码。代码如下:

/** * 开启WebSocket支持 * */ @Configuration public class WebSocketConfig { /** * 扫描并注册带有@ServerEndpoint注解的所有服务端 * @return */ @Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); } } package com.zntech.cpms.script.provider.config; import com.zntech.cpms.script.provider.constant.ConstantUtils; import com.zntech.cpms.script.provider.enums.MessageTypeEnum; import com.zntech.cpms.script.provider.enums.RunStatusEnum; import com.zntech.cpms.script.provider.pojo.ClientInfo; import com.zntech.cpms.script.provider.pojo.MessageRequest; import com.zntech.cpms.script.provider.pojo.ProjectTask; import com.zntech.cpms.script.provider.pojo.TaskFailInfo; import com.zntech.cpms.script.provider.service.ClientInfoService; import com.zntech.cpms.script.provider.service.ProjectTaskService; import com.zntech.cpms.script.provider.service.TaskFailInfoService; import com.zntech.cpms.script.provider.service.impl.MessageHandlerServiceImpl; import com.zntech.cpms.script.provider.util.CollectionsUtil; import com.zntech.cpms.script.provider.util.JacksonUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Component; import javax.validation.constraints.Max; import javax.websocket.*; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; import java.io.IOException; import java.lang.reflect.Array; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Collectors; /** * websocket服务端,多例的,一次websocket连接对应一个实例 * @ServerEndpoint 注解的值为URI,映射客户端输入的URL来连接到WebSocket服务器端 */ @Component @ServerEndpoint("/{name}") @Slf4j public class WebSocketServe { /** 用来记录当前在线连接数。设计成线程安全的。*/ private static AtomicInteger onlineCount = new AtomicInteger(0); /** 用于保存uri对应的连接服务,{uri:WebSocketServer},设计成线程安全的 */ private static ConcurrentHashMap webSocketServerMAP = new ConcurrentHashMap(); private Session session;// 与某个客户端的连接会话,需要通过它来给客户端发送数据 private String name; //客户端消息发送者 private String uri; //连接的uri /** * 连接建立成功时触发,绑定参数 * @param session * 可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据 * @param name * @param toName * @throws IOException */ @OnOpen public void onOpen(Session session, @PathParam("name") String name, @PathParam("toName") String toName) throws IOException { this.session = session; this.name = name; this.uri = session.getRequestURI().toString(); WebSocketServe webSocketServer = webSocketServerMAP.get(uri); if(webSocketServer != null){ //同样业务的连接已经在线,则把原来的挤下线。 webSocketServer.session.getBasicRemote().sendText(uri + "重复连接被挤下线了"); webSocketServer.session.close();//关闭连接,触发关闭连接方法onClose() } webSocketServerMAP.put(uri, this);//保存uri对应的连接服务 addOnlineCount(); // 在线数加1 } /** * 连接关闭时触发,注意不能向客户端发送消息了 * @throws IOException */ @OnClose public void onClose() throws IOException { webSocketServerMAP.remove(uri);//删除uri对应的连接服务 reduceOnlineCount(); // 在线数减1 } /** * 收到客户端消息后触发 * * @param message * 客户端发送过来的消息 * @throws IOException */ @OnMessage public void onMessage(String message) { log.info("收到消息:" + message); } /** * 通信发生错误时触发 * * @param session * @param error */ @OnError public void onError(Session session, Throwable error) { try { log.info("{}:通信发生错误,连接关闭",name); webSocketServerMAP.remove(uri);//删除uri对应的连接服务 }catch (Exception e){ } } /** * 获取在线连接数 * @return */ public static int getOnlineCount() { return onlineCount.get(); } /** * 原子性操作,在线连接数加一 */ public static void addOnlineCount() { onlineCount.getAndIncrement(); } /** * 原子性操作,在线连接数减一 */ public static void reduceOnlineCount() { onlineCount.getAndDecrement(); } }

我用的是gradle项目。当然别忘记了引入websocket的jar 附上gradle的jar.

implementation ("org.springframework.boot:spring-boot-starter-websocket:2.3.0.RELEASE") { exclude module: "spring-boot-starter-tomcat" }

下面开始重点总结: 一开始这个项目发布到服务器上面,只能支持一万的长连接。并且接口都访问不了。top命令看了linux的参数,发现CPU很内存都还很空。那么就不是linux的问题。查了很多资料,发现springboot项目默认的启动容器是tomcat,而tomcat默认支持1W的连接数量。超过就会拒绝。既然问题出在tomcat,那么现在就有两个方案。1、调大tomcat的连接数量。2、容器换成jetty。对于需要保持数十万的长连接,jetty无疑更适合作为启动容器。 启动容器替换成jetty,只需要在jar引用的的时候排除掉tomcat,并且加上jetty的jar.附上jar引用代码

implementation("org.springframework.boot:spring-boot-starter-web") { exclude group: 'org.springframework.boot', module: 'spring-boot-starter-tomcat' } implementation 'org.springframework.boot:spring-boot-starter-jetty:2.3.1.RELEASE' implementation ("org.springframework.boot:spring-boot-starter-websocket:2.3.0.RELEASE") { exclude module: "spring-boot-starter-tomcat" }

这里有个坑,引入websocket jar的时候一定也要排除tomcat的依赖。不然启动容器依然是tomcat。 现在这个代码放上linux服务器。按理来说应该可以支持起码好几万的长连接了吧。但是事与愿违,经过测试,发现居然只能建立1.6W+长连接。但是服务器的内存跟cpu明显还有空余。那么问题出在哪里?既然替换了容器,这个项目现在支持的长连接应该是看服务器的配置的。问题应该不在于框架了。那会不会是linux本身有什么限制。导致只能维持1.6W+的长连接呢? ulimit -a 参看linux的各种参数限制。 在这里插入图片描述 max locked memory (kbytes, -l) 16384 这个参数跟长连接的数量及其相似,初步猜测是不是这个参数的问题。 ulimit -l unlimited 把这个参数调成了无限制。现在再来测试长连接的数量。 已经可以达到5W+。因为测试工具的原因。没有做更高的压测,但是初步观察。保持10万的长连接应该是支持的。 附上永久修改 max locked memory 的命令

  1)、解除 Linux 系统的最大进程数和最大文件打开数限制: ​ vi /etc/security/limits.conf ​ \# 添加如下的行 root soft nofile 1048576 root hard nofile 1048576 * soft nofile 1048576 * hard nofile 1048576 ​ root soft memlock 102400 root hard memlock 102400 * soft memlock 102400 * hard memlock 102400

附上demo代码地址:https://gitee.com/hgs_ss/websocket



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3